export {}

// 1.类型推断 
namespace a {
    // 1.1 从右向左(从赋值中推断)
    let foo = 1; // let foo: number
    let bar = 'ikki'; // let bar: string

    // 1.2 底部流出(返回类型能被 return 语句推断)
    function add(a: number, b: number) { //function add(a: number, b: number): number
        return a + b;
    }
    let c = add(1,2);

    // 1.3 从左向右(函数参数类型/返回值类型也能通过赋值来推断)
    type Sum = (a: number, b: number) => number;
    let sum: Sum = (a, b) => { //type Sum = (a: number, b: number) => number
        return a + b;
    };

    // 1.4 结构化  推断规则也适用于结构化的存在(对象字面量)
    const person = {name: 'ikki',age: 11};
    let name = person.name;
    let age =person.age;
    age = 'hello'; // Error：不能把 'string' 类型赋值给 'number' 类型

    // 1.5 解构
    const person1 = {name1: 'ikki',age1: 11};
    let { name1,age1 } = person1;
    age1 = 'hello'; // Error：不能把 'string' 类型赋值给 'number' 类型
    //数组也一样
    const numbers = [1, 2, 3];
    numbers[0] = 'hello'; // Error：不能把 'string' 类型赋值给 'number' 类型

    // 1.6 DefaultProps 
    interface DefaultProps {
        name ?: string;
        age ?: number
    }
    let defaultProps: DefaultProps = {
        name :' ikki',
        age : 27
    }
    let props = {
        ...defaultProps,
        home: 'wenzhou'
    }
    type Props = typeof props 
    // =====================================>
    // type Props = {
    //     home: string;
    //     name?: string | undefined;
    //     age?: number | undefined;
    // }
    
    // 1.7 小心使用返回值(尽管 TypeScript 一般情况下能推断函数的返回值，但是它可能并不是你想要的)
    function addOne (a :any){
        return a+1
    }
    function sum5(a:number,b:number){
        return a + addOne(b)
    }
    type ret = ReturnType<typeof sum5>  //type ret = any
}
// 2.交叉类型
// 交叉类型(Intersection Types)是将多个类型合并为一个类型
// 这让我们可以把现有的多种类型叠加到一起成为一种类型，它包含了所需的所有类型的特性
namespace b {
    interface Bird {
        name: string,
        fly(): void
    }
    interface Person {
        name: string,
        talk(): void
    }
    type BirdPerson = Bird & Person
    let p : BirdPerson = {
        name:'ikki',
        fly(){},
        talk(){}
    }
    interface X {
        a: string;
        b: string;
    }
    interface Y {
        a: number;
        c: string
    }
    // 会把 a: string和 a: number 相交，得到never
    type XY = X & Y //先后顺序不影响
    // let p1 : XY = (c:'2',b:'2' )

    interface N {
        a: string | number;
        b: string;
    }
    interface R {
        a: number | boolean;
        c: string
    }
    let p1 :( N & R) = { a :1,b:'1',c:"1" }

    // 联合类型的交叉类型
    type Ta = string | number;
    type Tb = number | boolean;
    type Tc = Ta & Tb; //type Tc = number

    // mixin混入模式可以让你从两个对象中创建一个新对象，新对象会拥有着两个对象所有的功能
    interface AnyObject {
        [prop: string]: any;
    }
    function mixin<T extends AnyObject, U extends AnyObject>(one: T,two: U): T & U {
        const result = <T & U>{};
        for (let key in one) {
            (<T>result)[key] = one[key];
        }
        for (let key in two) {
            (<U>result)[key] = two[key];
        }
        return result;
    }
    const x = mixin({ name: "ikki" }, { age: 11 });
    console.log(x.name, x.age);
}

// 3.typeof 获取一个变量声明或对象的类型
namespace c {
    interface User{
        name: string;
        age: number;
    }
    const user: User= { name: 'huxianc', age: 25 };
    type UserType= typeof user; // -> User
    function func(x: number): number[] {return [x]}
    type Func = typeof func; // -> (x: number) => number[]
}

// 4.索引访问操作符 
// 可以通过[]获取一个类型的子类型
namespace d {
    interface Person{
        name:string;
        age:number;
        job:{
            name:string
        };
        interests:{name:string,level:number}[]
    }
    let Font : Person['job'] = { name:'ikki' }
}

// 5.keyof  索引类型查询操作符
namespace e {
    interface Person{
        name:string;
        age:number;
        gender:'male'|'female';
    }
    type PersonKey = keyof Person; //type PersonKey = 'name'|'age'|'gender';
    function getValueByKey(p:Person,key:PersonKey){
        return p[key];
    }
    let val = getValueByKey({name:'ikki',age:10,gender:'male'},'name');
    console.log(val);
    interface User{
        name: string;
        age: number;
      }
    type K1 = keyof User; // "name" | "age"
    type K2 = keyof User[]; // number | "length" | "toString" | "toLocaleString" | "pop" | "push" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | "splice" | "unshift" | "indexOf" | "lastIndexOf" | ... 14 more ... | "includes"
    // 对象的propName就算是number也会转换成string
    type K3 = keyof { [x: string]: User};  // string | number
    
      
}

// 6.映射类型
// 在定义的时候用in操作符去批量定义类型中的属性
namespace f {
    interface Person{
        name:string;
        age:number;
        gender:'male'|'female';
    }
    //批量把一个接口中的属性都变成可选的
    type PartPerson = {
        [key in keyof Person]?:Person[key]
    }
    let p1:PartPerson={};
    //也可以使用泛型
    type Part<T> = {
        [key in keyof T]?:T[key]
    }
    let p2:Part<Person>={};
}

// 7.条件类型
// 在定义泛型的时候能够添加进逻辑分支，以后泛型更加灵活
namespace f {
    // 7.1 定义条件类型
    interface Fish {name1: string}
    interface Water {name2: string}
    interface Bird {name3: string}
    interface Sky {name4: string}
    // 若 T 能够赋值给 Fish，那么类型是 Water,否则为 Sky
    type Condition<T> = T extends Fish ? Water : Sky
    let condition: Condition<Fish> = { name2: '水' }; //let condition: Water

    // 7.2 条件类型的分发
    let con1 : Condition<Fish|Bird> = {name2:''} //let con1: Water | Sky
    let con2 : Condition<Fish|Bird> = {name4:''}
    // 条件类型有一个特性,就是「分布式有条件类型」,但是分布式有条件类型是有前提的,条件类型里待检查的类型必须是naked type parameter
    // 必须是裸类型
    //none naked type
    type Condition2<T> = [T] extends [Fish] ? Water : Sky;
    let con3 : Condition2<Fish|Bird> = {name4:''}  //let con3: Sky
    // 找出T类型中U不包含的部分
    // never会被自动过滤
    type Diff<T, U> = T extends U ? never : T;
    type R = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"

    type Filter<T, U> = T extends U ? T : never; 
    type R1 = Filter<string | number | boolean, number>;   //type R1 = number


}
